package com.easyctba.core.service.impl;

import cn.hutool.core.io.FileUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.easyctba.common.core.domain.R;
import com.easyctba.common.core.utils.MapstructUtils;
import com.easyctba.common.core.utils.StringUtils;
import com.easyctba.common.mybatis.core.page.TableDataInfo;
import com.easyctba.common.mybatis.core.page.PageQuery;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.easyctba.common.redis.utils.RedisUtils;
import com.easyctba.core.algorithm.exec.FiasExec;
import com.easyctba.core.domain.*;
import com.easyctba.core.domain.bo.FiasAddPolylineBo;
import com.easyctba.core.domain.bo.FiasConfigBo;
import com.easyctba.core.domain.bo.FiasEncodeBo;
import com.easyctba.core.domain.vo.FiasPolylineVo;
import com.easyctba.core.domain.vo.FiasRadarVo;
import com.easyctba.core.enums.FiasRedisEnums;
import com.easyctba.core.enums.FiasStatus;
import com.easyctba.core.enums.StStatus;
import com.easyctba.core.util.ListUtil;
import com.easyctba.resource.api.RemoteFileService;
import com.easyctba.resource.api.domain.RemoteFile;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import com.easyctba.core.domain.bo.CtbaFiasBo;
import com.easyctba.core.domain.vo.CtbaFiasVo;
import com.easyctba.core.mapper.CtbaFiasMapper;
import com.easyctba.core.service.ICtbaFiasService;
import org.springframework.util.StopWatch;

import java.io.IOException;
import java.util.*;

/**
 * 弗兰德斯分析Service业务层处理
 *
 * @author Huang
 * @date 2024-03-18
 */
@RequiredArgsConstructor
@Service
public class CtbaFiasServiceImpl implements ICtbaFiasService {

    private final CtbaFiasMapper baseMapper;

    private final RemoteFileService remoteFileService;

    private final CtbaFiasPolylineServiceImpl ctbaFiasPolylineService;

    public CtbaFiasCategoryServiceImpl ctbaFiasCategoryService() {
        return SpringUtil.getBean(CtbaFiasCategoryServiceImpl.class);
    }

    private final CtbaFiasClassServiceImpl ctbaFiasClassService;

    /**
     * 查询弗兰德斯分析
     */
    @Override
    public CtbaFiasVo queryById(Long id){
        return baseMapper.selectVoById(id);
    }

    /**
     * 查询弗兰德斯分析列表
     */
    @Override
    public TableDataInfo<CtbaFiasVo> queryPageList(CtbaFiasBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<CtbaFias> lqw = buildQueryWrapper(bo);
        Page<CtbaFiasVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
        return TableDataInfo.build(result);
    }

    /**
     * 查询弗兰德斯分析列表
     */
    @Override
    public List<CtbaFiasVo> queryList(CtbaFiasBo bo) {
        LambdaQueryWrapper<CtbaFias> lqw = buildQueryWrapper(bo);
        return baseMapper.selectVoList(lqw);
    }

    private LambdaQueryWrapper<CtbaFias> buildQueryWrapper(CtbaFiasBo bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<CtbaFias> lqw = Wrappers.lambdaQuery();
        lqw.eq(bo.getProjectId() != null, CtbaFias::getProjectId, bo.getProjectId());
        lqw.eq(StringUtils.isNotBlank(bo.getFiasStatus()), CtbaFias::getFiasStatus, bo.getFiasStatus());
        return lqw;
    }

    /**
     * 新增弗兰德斯分析
     */
    @Override
    public Boolean insertByBo(CtbaFiasBo bo) {
        CtbaFias add = MapstructUtils.convert(bo, CtbaFias.class);
        validEntityBeforeSave(add);
        boolean flag = baseMapper.insert(add) > 0;
        if (flag) {
            bo.setId(add.getId());
        }
        return flag;
    }

    /**
     * 修改弗兰德斯分析
     */
    @Override
    public Boolean updateByBo(CtbaFiasBo bo) {
        CtbaFias update = MapstructUtils.convert(bo, CtbaFias.class);
        validEntityBeforeSave(update);
        return baseMapper.updateById(update) > 0;
    }

    /**
     * 保存前的数据校验
     */
    private void validEntityBeforeSave(CtbaFias entity){
        //TODO 做一些数据校验,如唯一约束
    }

    /**
     * 批量删除弗兰德斯分析
     */
    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if(isValid){
            //TODO 做一些业务上的校验,判断是否需要校验
        }
        return baseMapper.deleteBatchIds(ids) > 0;
    }

    @Override
    public R<CtbaFiasVo> getInfoByUserAndProject(Long id) {
        LambdaQueryWrapper<CtbaFias> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(CtbaFias::getProjectId,id);
        CtbaFiasVo ctbaFiasVo = baseMapper.selectVoOne(wrapper);
        return R.ok(ctbaFiasVo);
    }
    /**
     * 创建项目时初始化信息熵分析表
     * @param id 项目id
     * @return 是否成功
     */
    @Override
    public Boolean initFIAS(Long id) {
        //设置项目id和fias_status为upload
        CtbaFias ctbaFias = new CtbaFias();
        ctbaFias.setProjectId(id);
        ctbaFias.setFiasStatus(FiasStatus.upload);
        return this.baseMapper.insert(ctbaFias)>0;
    }

    @Override
    public Boolean fiasUploadVideo(Long id, String url) {
        CtbaFias ctbaFias = this.baseMapper.selectById(id);
        ctbaFias.setVideoUrl(url);
        return this.baseMapper.updateById(ctbaFias)>0;
    }

    @Override
    public R<Void> fiasConfig(FiasConfigBo bo) {
        Long id = bo.getId();
        CtbaFias ctbaFias = baseMapper.selectById(id);
        ctbaFias.setDuration(bo.getDuration());
        ctbaFias.setInterval(bo.getInterval());
        ctbaFias.setEndTime(bo.getEndTime());
        ctbaFias.setStartTime(bo.getStartTime());
        //更新状态
        ctbaFias.setFiasStatus(FiasStatus.ruler);
        boolean b=baseMapper.updateById(ctbaFias)>0;
        return b?R.ok():R.fail("更新FIAS分析状态失败");
    }

    @Override
    public R<Void> preUpload(Long id) {
        CtbaFias ctbaFias = baseMapper.selectById(id);
        ctbaFias.setFiasStatus(StStatus.upload);
        return baseMapper.updateById(ctbaFias)==1 ? R.ok() : R.fail("返回上一步失败");
    }

    @Override
    public R<Void> nextToEncode(Long id) {
        CtbaFias ctbaFias = baseMapper.selectById(id);
        ctbaFias.setFiasStatus(FiasStatus.encode);
        return baseMapper.updateById(ctbaFias)>=1?R.ok() : R.fail("更新状态失败");
    }

    @Override
    public R<Void> encode(FiasEncodeBo bo, Long id) {
        //开始计时
        var stopWatch = new StopWatch();
        stopWatch.start();
        //将记录列表上传到redis存储
        RedisUtils.setCacheListIfAbsent(FiasRedisEnums.FIAS_Redis_ID+id,bo.getCodeList());
        //生成FIAS编码表
        var fiasExec = new FiasExec();
        var ctbaFias = baseMapper.selectById(id);
        Long interval = ctbaFias.getInterval();
        var path = fiasExec.fiasTable(bo.getCodeList(), interval, id);
        //生成FIAS矩阵分析表
        List<Long> categoryIds = ctbaFiasCategoryService().listCategoryByFiasId(id);
        List<CtbaFiasClass> classList = ctbaFiasClassService.listClassByCategoryId(categoryIds);
        List<List<Object>> lists = fiasExec.initMatrix(classList);
        String matrixPath = fiasExec.matrixTable(bo.getCodeList(), id, lists);

        //生成雷达图信息
        Map<String, Object> radar = fiasExec.genRadar(bo.getCodeList(), classList);
        List<String> value = (List<String>) radar.get("value");
        String max = (String) radar.get("max");
        ctbaFias.setValue(value.toString());
        ctbaFias.setMax(max);
        //计算各大类的编码占比
        List<Long> size=ctbaFiasClassService.getClassSizeByCategoryIds(categoryIds);
        List<String> names= ctbaFiasCategoryService().getCategoryNames(categoryIds);
        List<Map<String, Object>> rate= fiasExec.getCategoryRate(value,size,names);
        //上传文件
        var file = FileUtil.file(path);
        var name = file.getName();
        var matrixFile = FileUtil.file(matrixPath);
        var matrixName=matrixFile.getName();
        byte[] fileBytes;
        byte[] fileBytes2;
        var fileUtil = new com.easyctba.core.util.FileUtil();
        try {
            fileBytes = fileUtil.file2ByteArray(path);
            fileBytes2 =fileUtil.file2ByteArray(matrixPath);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        RemoteFile upload = remoteFileService.upload(name, name, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", fileBytes);
        RemoteFile upload2 = remoteFileService.upload(matrixName, matrixName, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", fileBytes2);

        Long codeTable = upload.getOssId();//编码表的ossid
        Long matrixTable = upload2.getOssId();//矩阵表的ossid

        ctbaFias.setCodeTable(codeTable);
        ctbaFias.setMatrixTable(matrixTable);
        ctbaFias.setFiasStatus(FiasStatus.result);

        stopWatch.stop();
        long totalTimeMillis = stopWatch.getTotalTimeMillis();
        ctbaFias.setTime(totalTimeMillis);

        baseMapper.updateById(ctbaFias);
        return R.ok();
    }

    /**
     * 添加折线分析
     * @param bo 业务对象
     */
    @Override
    public R addPolyline(FiasAddPolylineBo bo) {
        //校验
        Boolean b=ctbaFiasPolylineService.checkName(bo.getName(),bo.getId());
        if (!b){
            return R.fail("折线名不能重复");
        }
        //编码列表
        List<String> cacheList = RedisUtils.getCacheList(FiasRedisEnums.FIAS_Redis_ID + bo.getId());
        //编码间隔
        CtbaFias ctbaFias = baseMapper.selectById(bo.getId());
        Long interval = ctbaFias.getInterval();
        //获取分类的num列表
        List<Long> classIds = bo.getIds();
        List<CtbaFiasClass> classList=ctbaFiasClassService.selectByIds(classIds);
        List<String> nums = classList.stream().map(CtbaFiasClass::getClassNum).toList();
        //按分钟进行统计
        int count= (int) (60 / interval);
        int t=count;
        List<List<Integer>> result = new ArrayList<>();
        int x=0;
        int y=0;
        for (String s : cacheList) {
            //如果当前值是需要分析的编码
            if (nums.contains(s)){
                y++;
            }
            t--;
            if (t<=0) {
                List<Integer> integers = new ArrayList<>();
                integers.add(x);
                integers.add(y);
                result.add(integers);
                x++;
                y=0;
                t=count;
            }
        }
        //插入折线表
        CtbaFiasPolyline ctbaFiasPolyline = new CtbaFiasPolyline();
        ctbaFiasPolyline.setFiasId(bo.getId());
        ctbaFiasPolyline.setName(bo.getName());
        ctbaFiasPolyline.setData(result.toString());
        ctbaFiasPolylineService.addPolyline(ctbaFiasPolyline);
        return R.ok();
    }

    /**
     * 获取折线信息
     * @param id fias id
     * @return
     */
    @Override
    public R getPolyline(Long id) {
        List<CtbaFiasPolyline> ctbaFiasPolylines= ctbaFiasPolylineService.getPolyline(id);
        ListUtil listUtil = new ListUtil();
        List<FiasPolylineVo> result = ctbaFiasPolylines.stream().map(i -> {
            FiasPolylineVo fiasPolylineVo = new FiasPolylineVo();
            String name = i.getName();
            String data = i.getData();
            List<List<Integer>> datas = listUtil.convertToList(data);
            fiasPolylineVo.setLegend(name);
            Map<String, Object> map = new HashMap<>();
            map.put("name", name);
            map.put("type", "line");
            map.put("data", datas);
            fiasPolylineVo.setSeries(map);
            return fiasPolylineVo;
        }).toList();

        return R.ok(result);
    }

    @Override
    public R<Void> nextToDownload(Long id) {
        CtbaFias ctbaFias = baseMapper.selectById(id);
        ctbaFias.setFiasStatus(FiasStatus.download);
        return baseMapper.updateById(ctbaFias)>=1?R.ok() : R.fail("更新状态失败");
    }

    @Override
    public R<Void> preToPolyline(Long id) {
        CtbaFias ctbaFias = baseMapper.selectById(id);
        ctbaFias.setFiasStatus(FiasStatus.result);
        return baseMapper.updateById(ctbaFias)>=1?R.ok():R.fail("更新状态失败");
    }

    @Override
    public R getRadar(Long id) {
        //获取编码列表
        CtbaFias ctbaFias = baseMapper.selectById(id);
        List<Long> categoryList = ctbaFiasCategoryService().listCategoryByFiasId(id);
        List<CtbaFiasClass> classList = ctbaFiasClassService.listClassByCategoryId(categoryList);
        //设置返回VO
        FiasRadarVo fiasRadarVo = new FiasRadarVo();
        ListUtil listUtil = new ListUtil();
        List<String> valueList = listUtil.convertStringToArrayList(ctbaFias.getValue());
        fiasRadarVo.setValue(valueList);
        List<Map<String, String>> indicator = classList.stream().map(i -> {
            Map<String, String> map = new HashMap<>();
            map.put("name", i.getClassName());
            map.put("max", ctbaFias.getMax());
            return map;
        }).toList();
        fiasRadarVo.setIndicator(indicator);
        return R.ok(fiasRadarVo);
    }

    @Override
    public R result(Long id) {
        CtbaFias ctbaFias = baseMapper.selectById(id);
        //小类占比值信息
        ListUtil listUtil = new ListUtil();
        List<String> value = listUtil.convertStringToArrayList(ctbaFias.getValue());
        //计算各大类占比
        FiasExec fiasExec = new FiasExec();
        //大类id
        List<Long> categoryIds = ctbaFiasCategoryService().listCategoryByFiasId(id);
        //大类size
        List<Long> size=ctbaFiasClassService.getClassSizeByCategoryIds(categoryIds);
        //大类名size
        List<String> names= ctbaFiasCategoryService().getCategoryNames(categoryIds);
        //比率结果
        List<Map<String, Object>> rate= fiasExec.getCategoryRate(value,size,names);

        return R.ok(rate);
    }
}
